]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Backup commit - code isn't yet working.
authorTed Lemon <source@isc.org>
Sun, 14 Nov 1999 00:42:57 +0000 (00:42 +0000)
committerTed Lemon <source@isc.org>
Sun, 14 Nov 1999 00:42:57 +0000 (00:42 +0000)
server/failover.c

index d2aa3f1dd00bc45dcf96ea49440693a667bdb5e3..4c22197931442bdfe5bddffaaf238dff56d741ff 100644 (file)
@@ -3,7 +3,7 @@
    Failover protocol support code... */
 
 /*
- * Copyright (c) 1996-1999 Internet Software Consortium.
+ * Copyright (c) 1999-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
 
 #ifndef lint
 static char copyright[] =
-"$Id: failover.c,v 1.2 1999/03/16 05:50:46 mellon Exp $ Copyright (c) 1999 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: failover.c,v 1.3 1999/11/14 00:42:57 mellon Exp $ Copyright (c) 1999 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
 
 #if defined (FAILOVER_PROTOCOL)
 static struct hash_table *failover_hash;
+static isc_result_t do_a_failover_option (omapi_connection_object_t *,
+                                         dhcp_failover_link_t *);
 
 void enter_failover_peer (peer)
        struct failover_peer *peer;
@@ -46,4 +48,879 @@ struct failover_peer *find_failover_peer (name)
        return peer;
 }
 
+/* The failover protocol has three objects associated with it.  For
+   each failover partner declaration in the dhcpd.conf file, primary
+   or secondary, there is a failover_state object.  For any primary or
+   secondary state object that has a connection to its peer, there is
+   also a failover_link object, which has its own input state seperate
+   from the failover protocol state for managing the actual bytes
+   coming in off the wire.  Finally, there will be one listener object
+   for every distinct port number associated with a secondary
+   failover_state object.  Normally all secondary failover_state
+   objects are expected to listen on the same port number, so there
+   need be only one listener object, but if different port numbers are
+   specified for each failover object, there could be as many as one
+   listener object for each secondary failover_state object. */
+
+/* This, then, is the implemention of the failover link object. */
+
+isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
+{
+       isc_result_t status;
+       dhcp_failover_link_t *obj;
+       char *peer_name;
+       unsigned long port;
+       omapi_typed_data_t *value = (omapi_typed_data_t *)0;
+
+       status = omapi_get_value_str (h, (omapi_object_t *)0,
+                                     "remote-port", &value);
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       status = omapi_get_int_value (&port, value);
+       omapi_typed_data_dereference (&value, "dhcp_failover_link_initiate");
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       status = omapi_get_value_str (h, (omapi_object_t *)0,
+                                     "remote-peer", &value);
+       if (status != ISC_R_SUCCESS)
+               return status;
+       if (value -> type != omapi_datatype_string &&
+           value -> type != omapu_datatype_data) {
+               omapi_typed_data_dereference (&value,
+                                             "dhcp_failover_link_initiate");
+               return ISC_R_INVALIDARG;
+       }
+
+       /* Save the name. */
+       peer_name = malloc (value -> u.buffer.len + 1);
+       if (!peer_name) {
+               omapi_typed_data_dereference (&value,
+                                             "dhcp_failover_link_initiate");
+               return ISC_R_NOMEMORY;
+       }
+
+       memcpy (peer_name, value -> u.buffer.data, value -> u.buffer.len);
+       peer_name [value -> u.buffer.len] = 0;
+       omapi_typed_data_dereference (&value, "dhcp_failover_link_initiate");
+
+       obj = (dhcp_failover_link_t *)malloc (sizeof *obj);
+       if (!obj)
+               return ISC_R_NOMEMORY;
+       memset (obj, 0, sizeof *obj);
+       obj -> refcnt = 1;
+       obj -> type = dhcp_type_failover_link;
+       obj -> peer_name = peer_name;
+       obj -> port = port;
+
+       status = omapi_connect ((omapi_object_t *)obj, server_name, port);
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_link_initiate");
+               return status;
+       }
+       status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
+                                        "dhcp_failover_link_initiate");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_link_initiate");
+               return status;
+       }
+       status = omapi_object_reference (&obj -> inner, h,
+                                        "dhcp_failover_link_initiate");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_link_initiate");
+               return status;
+       }
+
+       /* Send the introductory message. */
+       status = dhcp_failover_send_connect ((omapi_object_t *)obj);
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_link_initiate");
+               return status;
+       }
+
+       omapi_object_dereference ((omapi_object_t **)&obj,
+                                 "omapi_protocol_accept");
+       return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
+                                       const char *name, va_list ap)
+{
+       isc_result_t status;
+       dhcp_failover_link_t *link;
+       omapi_object_t *c;
+       u_int16_t nlen;
+       u_int32_t vlen;
+
+       if (h -> type != dhcp_type_failover_link) {
+               /* XXX shouldn't happen.   Put an assert here? */
+               return ISC_R_UNEXPECTED;
+       }
+       link = (dhcp_failover_link_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 dhcp_flink_start:
+               p -> state = dhcp_flink_message_length_wait;
+               if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
+                       break;
+             case dhcp_flink_message_length_wait:
+               p -> state = dhcp_flink_message_wait;
+               memset (link -> incoming_message, 0, sizeof (link -> imsg));
+               /* Get the length: */
+               omapi_connection_get_uint16 (c, &link -> imsg_len);
+               link -> imsg_count = 0; /* Bytes read. */
+               
+               /* Maximum of 2048 bytes in any failover message. */
+               if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
+                     dhcp_flink_fail:
+                       link -> state = dhcp_flink_disconnected;
+                       omapi_disconnect (c, 1);
+                       /* XXX just blow away the protocol state now?
+                          XXX or will disconnect blow it away? */
+                       return ISC_R_UNEXPECTED;
+               }
+
+               if ((omapi_connection_require (c, link -> imsg_len)) !=
+                   ISC_R_SUCCESS)
+                       break;
+             case dhcp_flink_message_wait:
+               /* Read in the message.  At this point we have the
+                  entire message in the input buffer.  For each
+                  incoming value ID, set a bit in the bitmask
+                  indicating that we've gotten it.  Maybe flag an
+                  error message if the bit is already set.  Once
+                  we're done reading, we can check the bitmask to
+                  make sure that the required fields for each message
+                  have been included. */
+
+               link -> imsg_count += 2;        /* Count the length as read. */
+
+               /* Get message type. */
+               omapi_connection_copyout (&link -> imsg -> type, c, 1);
+               link -> imsg_count++;
+
+               /* Get message payload offset. */
+               omapi_connection_copyout (&link -> imsg_payoff, c, 1);
+               link -> imsg_count++;
+
+               /* Get message time. */
+               omapi_connection_get_uint32 (c, &link -> imsg -> time);
+               link -> imsg_count += 4;
+
+               /* Get transaction ID. */
+               omapi_connection_get_uint32 (c, &link -> imsg -> xid);
+               link -> imsg_count += 4;
+
+               /* Skip over any portions of the message header that we
+                  don't understand. */
+               if (link -> payoff - link -> imsg_count) {
+                       omapi_connection_copyout ((unsigned char *)0, c,
+                                                 (link -> payoff -
+                                                  link -> imsg_count));
+                       link -> imsg_count = link -> payoff;
+               }
+                               
+               /* Get transaction ID. */
+               omapi_connection_get_uint32 (c, &link -> imsg -> xid);
+               link -> imsg_count += 4;
+
+               /* Now start sucking options off the wire. */
+               while (link -> imsg_count < link -> imsg_len) {
+                       if (do_a_failover_option (c, link) != ISC_R_SUCCESS)
+                               goto dhcp_flink_fail;
+               }
+
+               /* Once we have the entire message, and we've validated
+                  it as best we can here, pass it to the parent. */
+               omapi_signal_in (h -> outer, "message", link);
+               break;
+
+             default:
+               /* XXX should never get here.   Assertion? */
+               break;
+       }
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t do_a_failover_option (c, link)
+       omapi_connection_object_t *c;
+       dhcp_failover_link_t *link;
+{
+       u_int16_t option_code;
+       u_int16_t option_len;
+       char *op;
+       int op_size;
+       int op_count;
+       
+       if (link -> imsg_count + 2 > link -> imsg_len) {
+               log_error ("FAILOVER: message overflow at option code.");
+               return ISC_R_PROTOCOLERROR;
+       }
+
+       /* Get option code. */
+       omapi_connection_get_uint16 (c, &option_code);
+       link -> imsg_count += 2;
+       
+       /* Get option length. */
+       omapi_connection_get_uint16 (c, &option_len);
+       link -> imsg_count += 2;
+
+       if (link -> imsg_count + option_len > link -> imsg_len) {
+               log_error ("FAILOVER: message overflow at %s",
+                          " length.");
+               return ISC_R_PROTOCOLERROR;
+       }
+
+       /* If it's an unknown code, skip over it. */
+       if (option_code > FTO_MAX) {
+#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
+               log_debug ("  option code %d len %d (not recognized)",
+                          option_code, option_len);
+#endif
+               omapi_connection_copyout ((unsigned char *)0, c, option_len);
+               link -> imsg_count = += option_len;
+               return ISC_R_SUCCESS;
+       }
+
+       /* If it's the digest, do it now. */
+       if (ft_options [option_code].type == FT_DIGEST) {
+               link -> imsg_count += option_len;
+               if (link -> imsg_count != link -> imsg_len) {
+                       log_error ("FAILOVER: digest not at end of message");
+                       return ISC_R_PROTOCOLERROR;
+               }
+#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
+               log_debug ("  option %s len %d",
+                          ft_options [option_code].name, option_len);
+#endif
+               /* For now, just dump it. */
+               omapi_connection_copyout ((unsigned char *)0, c, option_len);
+               return ISC_R_SUCCESS;
+       }
+       
+       /* Only accept an option once. */
+       if (imsg -> options_present & ft_options [option_code].bit) {
+               log_error ("FAILOVER: duplicate option %s",
+                          ft_options [option_code].name);
+               return ISC_R_PROTOCOLERROR;
+       }
+
+       /* Make sure the option is appropriate for this type of message.
+          Really, any option is generally allowed for any message, and the
+          cases where this is not true are too complicated to represent in
+          this way - what this code is doing is to just avoid saving the
+          value of an option we don't have any way to use, which allows
+          us to make the failover_message structure smaller. */
+       if (ft_options [option_code].bit &&
+           !(fto_allowed [option_code] & ft_options [option_code].bit)) {
+               omapi_connection_copyout ((unsigned char *)0, c, option_len);
+               link -> imsg_count = += option_len;
+               return ISC_R_SUCCESS;
+       }               
+
+       /* Figure out how many elements, how big they are, and where
+          to store them. */
+       if (ft_options [option_code].num_present) {
+               /* If this option takes a fixed number of elements,
+                  we expect the space for them to be preallocated,
+                  and we can just read the data in. */
+
+               op = ((char *)&link -> imsg) + ft_options [option_code].offset;
+               op_size = ft_sizes [ft_options [option_code].type];
+               op_count = ft_options [option_code].num_present;
+
+               if (option_length != op_size * op_count) {
+                       log_error ("FAILOVER: option size (%d:%d), option %s",
+                                  option_length,
+                                  (ft_sizes [ft_options [option_code].type] *
+                                   ft_options [option_code].num_present),
+                                  ft_options [option_code].name);
+                       return ISC_R_PROTOCOLERROR;
+               }
+       } else {
+               struct failover_option *fo;
+
+               /* FT_DDNS* are special - one or two bytes of status
+                  followed by the client FQDN. */
+               if (ft_options [option_code].type == FT_DDNS1 ||
+                   ft_options [option_code].type == FT_DDNS1) {
+                       struct failover_ddns *ddns =
+                               ((struct failover_ddns *)
+                                (((char *)&link -> imsg) +
+                                 ft_options [option_code].offset));
+
+                       op_count = (ft_options [option_code].type == FT_DDNS1
+                                   ? 1 : 2);
+
+                       omapi_connection_copyout (&ddns -> codes [0],
+                                                 c, op_count);
+                       if (op_count == 1)
+                               ddns -> codes [1] = 0;
+                       op_size = 1;
+                       op_count = option_length - op_count;
+
+                       ddns -> length = op_count;
+                       ddns -> data = malloc (op_count);
+                       if (!ddns -> data) {
+                               log_error ("FAILOVER: no memory getting%s(%d)",
+                                          " DNS data ", op_count);
+
+                               /* Actually, NO_MEMORY, but if we lose here
+                                  we have to drop the connection. */
+                               return ISC_R_PROTOCOLERROR;
+                       }
+                       omapi_connection_copyout (ddns -> data, c, op_count);
+                       goto out;
+               }
+       } else {
+               /* A zero for num_present means that any number of
+                  elements can appear, so we have to figure out how
+                  many we got from the length of the option, and then
+                  fill out a failover_option structure describing the
+                  data. */
+               op_size = ft_sizes [ft_options [option_code].type];
+
+               /* Make sure that option data length is a multiple of the
+                  size of the data type being sent. */
+               if (op_size > 1 && option_length % op_size) {
+                       log_error ("FAILOVER: option_length %d not %s%d",
+                                  option_length, "multiple of ", option_size);
+                       return ISC_R_PROTOCOLERROR;
+               }
+
+               op_count = option_length / op_size;
+               
+               fo = ((struct failover_option *)
+                     (((char *)&link -> imsg) +
+                      ft_options [option_code].offset));
+
+               fo -> count = op_count;
+               fo -> data = malloc (option_length);
+               if (!fo -> data) {
+                       log_error ("FAILOVER: no memory getting %s (%d)",
+                                  "option data", op_count);
+
+                       return ISC_R_PROTOCOLERROR;
+               }                       
+               op = fo -> data;
+       }
+
+       /* For single-byte message values and multi-byte values that
+           don't need swapping, just read them in all at once. */
+       if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
+               omapi_connection_copyout ((unsigned char *)op, c, option_len);
+               goto out;
+       }
+
+       /* For values that require swapping, read them in one at a time
+          using routines that swap bytes. */
+       for (i = 0; i < op_count; i++) {
+               switch (ft_options [option_code].type) {
+                     case FT_UINT32:
+                       omapi_connection_get_uint32 (c, op);
+                       op += 4;
+                       break;
+                       
+                     case FT_UINT16:
+                       omapi_connection_get_uint16 (c, op);
+                       op += 2;
+                       break;
+                       
+                     default:
+                       /* Everything else should have been handled
+                          already. */
+                       log_error ("FAILOVER: option %s: bad type %d",
+                                  ft_options [option_code].name,
+                                  ft_options [option_code].type);
+                       return ISC_R_PROTOCOLERROR;
+               }
+       }
+      out:
+       /* Remember that we got this option. */
+       link -> options_present |= ft_options [option_code].bit;
+       return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_link_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;
+
+       /* Never valid to set these. */
+       if (!omapi_ds_strcmp (name, "port") ||
+           !omapi_ds_strcmp (name, "link-name") ||
+           !omapi_ds_strcmp (name, "link-state"))
+               return ISC_R_NOPERM;
+
+       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 dhcp_failover_link_get_value (omapi_object_t *h,
+                                          omapi_object_t *id,
+                                          omapi_data_string_t *name,
+                                          omapi_value_t **value)
+{
+       dhcp_failover_link_t *link;
+
+       if (h -> type != omapi_type_protocol)
+               return ISC_R_INVALIDARG;
+       link = (dhcp_failover_link_t *)h;
+       
+       if (!omapi_ds_strcmp (name, "port")) {
+               return omapi_make_int_value (value, name, link -> port,
+                                            "dhcp_failover_link_get_value");
+       } else if (!omapi_ds_strcmp (name, "link-name")) {
+               return omapi_make_string_value
+                       (value, name, link -> peer_name,
+                        "dhcp_failover_link_get_value");
+       } else if (!omapi_ds_strcmp (name, "link-state")) {
+               if (link -> state < 0 ||
+                   link -> state >= dhcp_failover_link_state_max)
+                       return omapi_make_string_value
+                               (value, name, "invalid link state",
+                                "dhcp_failover_link_get_value");
+               return omapi_make_string_value
+                       (value, name,
+                        dhcp_failover_link_state_names [link -> state],
+                        "dhcp_failover_link_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 dhcp_failover_link_destroy (omapi_object_t *h, const char *name)
+{
+       dhcp_failover_link_t *p;
+       if (h -> type != dhcp_type_failover_link)
+               return ISC_R_INVALIDARG;
+       p = (dhcp_failover_link_object_t *)h;
+       if (p -> message)
+               omapi_object_dereference ((omapi_object_t **)&p -> message,
+                                         name);
+       return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+   specified connection. */
+
+isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
+                                             omapi_object_t *id,
+                                             omapi_object_t *p)
+{
+       dhcp_failover_link_t *link;
+
+       if (h -> type != omapi_type_protocol)
+               return ISC_R_INVALIDARG;
+       link = (dhcp_failover_link_t *)h;
+       
+       status = omapi_connection_put_name (c, "port");
+       if (status != ISC_R_SUCCESS)
+               return status;
+       status = omapi_put_uint32 (c, sizeof (int));
+       if (status != ISC_R_SUCCESS)
+               return status;
+       status = omapi_put_uint32 (c, link -> port);
+       if (status != ISC_R_SUCCESS)
+               return status;
+       
+       status = omapi_connection_put_name (c, "link-name");
+       if (status != ISC_R_SUCCESS)
+               return status;
+       status = omapi_connection_put_string (c, link -> peer_name);
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       status = omapi_connection_put_name (c, "link-state");
+       if (status != ISC_R_SUCCESS)
+               return status;
+       if (link -> state < 0 ||
+           link -> state >= dhcp_failover_link_state_max)
+               status = omapi_connection_put_string (c, "invalid link state");
+       else
+               status = (omapi_connection_put_string
+                         (c, dhcp_failover_link_state_names [link -> state]));
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       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 dhcp_failover_listen (omapi_object_t *h);
+{
+       isc_result_t status;
+       dhcp_failover_listener_object_t *obj;
+       unsigned long port;
+       omapi_value_t *value = (omapi_value_t *)0;
+
+       status = omapi_get_value_str (h, (omapi_object_t *)0,
+                                     "local-port", &value);
+       if (status != ISC_R_SUCCESS)
+               return status;
+       
+       status = omapi_get_int_value (&port, value);
+       omapi_typed_data_dereference (&value, "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       obj = (dhcp_failover_listener_object_t *)malloc (sizeof *obj);
+       if (!obj)
+               return ISC_R_NOMEMORY;
+       memset (obj, 0, sizeof *obj);
+       obj -> refcnt = 1;
+       obj -> type = dhcp_type_failover_listener;
+       obj -> port = port;
+       
+       status = omapi_listen ((omapi_object_t *)obj, port, 1);
+       omapi_object_dereference ((omapi_object_t **)&obj,
+                                 "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
+                                        "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_listen");
+               return status;
+       }
+       status = omapi_object_reference (&obj -> inner, h,
+                                        "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_listen");
+               return status;
+       }
+
+       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 dhcp_failover_listener_signal (omapi_object_t *o,
+                                           const char *name, va_list ap)
+{
+       isc_result_t status;
+       omapi_connection_object_t *c;
+       omapi_protocol_object_t *obj;
+       dhcp_failover_listener_object_t *p;
+       dhcp_failover_state_object_t *state;
+       char *peer_name;
+
+       if (!o || o -> type != dhcp_type_failover_listener)
+               return ISC_R_INVALIDARG;
+       p = (dhcp_failover_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_connection_object_t *);
+       if (!c || c -> type != omapi_type_connection)
+               return ISC_R_INVALIDARG;
+
+       /* See if we can find a secondary failover_state object that
+          matches this connection. */
+       for (state = states; state; state = state -> next) {
+               struct hostent *he;
+               int hix;
+               struct in_addr ia;
+
+               if (inet_aton (state -> remote_peer, &ia)) {
+                       if (ia == c -> remote_addr.sin_addr)
+                               break;
+               } else {
+                       he = gethostbyname (state -> remote_peer);
+                       if (!he)
+                               continue;
+                       for (hix = 0; he -> h_addr_list [hix]; hix++) {
+                               if (!memcmp (he -> h_addr_list [hix],
+                                            &c -> remote_addr.sin_addr,
+                                            sizeof c -> remote_addr.sin_addr))
+                                       break;
+                       }
+                       if (he -> h_addr_list [hix])
+                               break;
+               }
+       }               
+
+       /* If we can't find a failover protocol state for this remote
+          host, drop the connection */
+       if (!state) {
+               /* XXX Send a refusal message first?
+                  XXX Look in protocol spec for guidance. */
+               /* XXX An error message from a connect signal should
+                  XXX drop the connection - make sure this is what
+                  XXX actually happens! */
+               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;
+       peer_name = malloc (strlen (state -> remote_peer) + 1);
+       if (!peer_name)
+               return ISC_R_NOMEMORY;
+       strcpy (peer_name, state -> remote_peer);
+       obj -> peer_name = peer_name;
+       obj -> port = ntohs (c -> remote_addr.sin_port);
+
+       status = omapi_object_reference (&obj -> outer, c,
+                                        "dhcp_failover_listener_signal");
+       if (status != ISC_R_SUCCESS) {
+             lose:
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_listener_signal");
+               omapi_disconnect (c, 1);
+               return status;
+       }
+
+       status = omapi_object_reference (&c -> inner, (omapi_object_t *)obj,
+                                        "dhcp_failover_listener_signal");
+       if (status != ISC_R_SUCCESS)
+               goto lose;
+
+       /* Notify the master state machine of the arrival of a new
+           connection. */
+       status = omapi_signal (state, "connect", obj);
+       if (status != ISC_R_SUCCESS)
+               goto lose;
+
+       omapi_object_dereference ((omapi_object_t **)&obj,
+                                 "dhcp_failover_listener_signal");
+       return status;
+}
+
+isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
+                                               omapi_object_t *id,
+                                               omapi_data_string_t *name,
+                                               omapi_typed_data_t *value)
+{
+       if (h -> type != dhcp_type_failover_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 dhcp_failover_listener_get_value (omapi_object_t *h,
+                                               omapi_object_t *id,
+                                               omapi_data_string_t *name,
+                                               omapi_value_t **value)
+{
+       if (h -> type != dhcp_type_failover_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 dhcp_failover_listener_destroy (omapi_object_t *h,
+                                             const char *name)
+{
+       if (h -> type != dhcp_type_failover_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 dhcp_failover_listener_stuff (omapi_object_t *c,
+                                          omapi_object_t *id,
+                                          omapi_object_t *p)
+{
+       int i;
+
+       if (p -> type != dhcp_type_failover_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;
+}
+
+/* Set up master state machine for the failover protocol. */
+
+isc_result_t dhcp_failover_register (omapi_object_t *h);
+{
+       isc_result_t status;
+       dhcp_failover_state_object_t *obj;
+       unsigned long port;
+       omapi_value_t *value = (omapi_value_t *)0;
+
+       status = omapi_get_value_str (h, (omapi_object_t *)0,
+                                     "local-port", &value);
+       if (status != ISC_R_SUCCESS)
+               return status;
+       
+       status = omapi_get_int_value (&port, value);
+       omapi_typed_data_dereference (&value, "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       obj = (dhcp_failover_state_object_t *)malloc (sizeof *obj);
+       if (!obj)
+               return ISC_R_NOMEMORY;
+       memset (obj, 0, sizeof *obj);
+       obj -> refcnt = 1;
+       obj -> type = dhcp_type_failover_state;
+       obj -> port = port;
+       
+       status = omapi_listen ((omapi_object_t *)obj, port, 1);
+       omapi_object_dereference ((omapi_object_t **)&obj,
+                                 "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS)
+               return status;
+
+       status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
+                                        "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_listen");
+               return status;
+       }
+       status = omapi_object_reference (&obj -> inner, h,
+                                        "dhcp_failover_listen");
+       if (status != ISC_R_SUCCESS) {
+               omapi_object_dereference ((omapi_object_t **)&obj,
+                                         "dhcp_failover_listen");
+               return status;
+       }
+
+       return status;
+}
+
+/* Signal handler for protocol state machine. */
+
+isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
+                                        const char *name, va_list ap)
+{
+       isc_result_t status;
+       omapi_connection_object_t *c;
+       omapi_protocol_object_t *obj;
+       dhcp_failover_state_object_t *p;
+       dhcp_failover_state_object_t *state;
+       char *peer_name;
+
+       if (!o || o -> type != dhcp_type_failover_state)
+               return ISC_R_INVALIDARG;
+       p = (dhcp_failover_state_object_t *)o;
+
+       /* Not a signal we recognize? */
+       if (strcmp (name, "connect") &&
+           strcmp (name, "disconnect") &&
+           strcmp (name, "message")) {
+               if (p -> inner && p -> inner -> type -> signal_handler)
+                       return (*(p -> inner -> type -> signal_handler))
+                               (p -> inner, name, ap);
+               return ISC_R_NOTFOUND;
+       }
+
+
+}
+
+isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
+                                               omapi_object_t *id,
+                                               omapi_data_string_t *name,
+                                               omapi_typed_data_t *value)
+{
+       if (h -> type != dhcp_type_failover_state)
+               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 dhcp_failover_state_get_value (omapi_object_t *h,
+                                               omapi_object_t *id,
+                                               omapi_data_string_t *name,
+                                               omapi_value_t **value)
+{
+       if (h -> type != dhcp_type_failover_state)
+               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 dhcp_failover_state_destroy (omapi_object_t *h,
+                                             const char *name)
+{
+       if (h -> type != dhcp_type_failover_state)
+               return ISC_R_INVALIDARG;
+       return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+   specified connection. */
+
+isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
+                                          omapi_object_t *id,
+                                          omapi_object_t *p)
+{
+       int i;
+
+       if (p -> type != dhcp_type_failover_state)
+               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;
+}
+
+
 #endif /* defined (FAILOVER_PROTOCOL) */