1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 1996, 1997, 1998, 1999 Internet Software Consortium.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
|
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
|
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
|
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
|
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
|
|
* SOFTWARE.
|
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/* $Id: connection.c,v 1.2 1999/11/02 04:01:32 tale Exp $ */
|
|
|
|
|
|
|
|
/* Principal Author: Ted Lemon */
|
1999-10-27 22:24:32 +00:00
|
|
|
|
|
|
|
/*
|
1999-11-02 04:01:34 +00:00
|
|
|
* Subroutines for dealing with connections.
|
1999-10-27 22:24:32 +00:00
|
|
|
*/
|
1999-11-02 04:01:34 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h> /* F_SETFL, O_NONBLOCK */
|
|
|
|
#include <stddef.h> /* NULL */
|
|
|
|
#include <stdlib.h> /* malloc, free */
|
|
|
|
#include <string.h> /* memset */
|
|
|
|
#include <unistd.h> /* close */
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
#include <isc/assertions.h>
|
|
|
|
#include <isc/netdb.h>
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
#include <omapi/omapip_p.h>
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
omapi_connect(omapi_object_t *c, const char *server_name, int port) {
|
1999-10-27 22:24:32 +00:00
|
|
|
struct hostent *he;
|
|
|
|
int hix;
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t result;
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *obj;
|
|
|
|
int flag;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
obj = (omapi_connection_object_t *)malloc(sizeof(*obj));
|
|
|
|
if (obj == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
memset(obj, 0, sizeof(*obj));
|
|
|
|
obj->refcnt = 1;
|
|
|
|
obj->type = omapi_type_connection;
|
|
|
|
|
|
|
|
omapi_object_reference(&c->outer, (omapi_object_t *)obj,
|
|
|
|
"omapi_protocol_connect");
|
|
|
|
|
|
|
|
omapi_object_reference(&obj->inner, c, "omapi_protocol_connect");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) == 0) {
|
|
|
|
/*
|
|
|
|
* 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 == NULL) {
|
|
|
|
omapi_object_dereference((omapi_object_t **)&obj,
|
|
|
|
"omapi_connect");
|
|
|
|
return (ISC_R_NOTFOUND);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
hix = 1;
|
1999-11-02 04:01:34 +00:00
|
|
|
memcpy (&obj->remote_addr.sin_addr,
|
|
|
|
he->h_addr_list[0],
|
|
|
|
sizeof(obj->remote_addr.sin_addr));
|
1999-10-27 22:24:32 +00:00
|
|
|
} else
|
1999-11-02 04:01:34 +00:00
|
|
|
he = NULL;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
#ifdef ISC_NET_HAVESALEN
|
|
|
|
obj->remote_addr.sin_len = sizeof(struct sockaddr_in);
|
1999-10-27 22:24:32 +00:00
|
|
|
#endif
|
1999-11-02 04:01:34 +00:00
|
|
|
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");
|
1999-10-27 22:24:32 +00:00
|
|
|
if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
|
1999-11-02 04:01:34 +00:00
|
|
|
return (ISC_R_NORESOURCES);
|
|
|
|
return (ISC_R_UNEXPECTED);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Set the SO_REUSEADDR flag (this should not fail).
|
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
flag = 1;
|
1999-11-02 04:01:34 +00:00
|
|
|
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);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)) < 0) {
|
|
|
|
if (he == NULL || he->h_addr_list[hix] == NULL) {
|
|
|
|
omapi_object_dereference((omapi_object_t **)&obj,
|
|
|
|
"omapi_connect");
|
1999-10-27 22:24:32 +00:00
|
|
|
if (errno == ECONNREFUSED)
|
1999-11-02 04:01:34 +00:00
|
|
|
return (ISC_R_CONNREFUSED);
|
1999-10-27 22:24:32 +00:00
|
|
|
if (errno == ENETUNREACH)
|
1999-11-02 04:01:34 +00:00
|
|
|
return (ISC_R_NETUNREACH);
|
|
|
|
return (ISC_R_UNEXPECTED);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
1999-11-02 04:01:34 +00:00
|
|
|
memcpy(&obj->remote_addr.sin_addr, he->h_addr_list[hix++],
|
|
|
|
sizeof(obj->remote_addr.sin_addr));
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
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)
|
|
|
|
result = ISC_R_UNEXPECTED;
|
|
|
|
else
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
if (fcntl(obj->socket, F_SETFL, O_NONBLOCK) < 0)
|
|
|
|
result = ISC_R_UNEXPECTED;
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = omapi_register_io_object((omapi_object_t *)obj,
|
|
|
|
omapi_connection_readfd,
|
|
|
|
omapi_connection_writefd,
|
|
|
|
omapi_connection_reader,
|
|
|
|
omapi_connection_writer,
|
|
|
|
omapi_connection_reaper);
|
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
omapi_object_dereference((omapi_object_t **)&obj,
|
|
|
|
"omapi_connect");
|
|
|
|
|
|
|
|
return (result);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Disconnect a connection object from the remote end. If force is true,
|
|
|
|
* close the connection immediately. Otherwise, shut down the receiving end
|
|
|
|
* but allow any unsent data to be sent before actually closing the socket.
|
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
void
|
|
|
|
omapi_disconnect(omapi_object_t *h, isc_boolean_t force) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL);
|
|
|
|
|
1999-10-27 22:24:32 +00:00
|
|
|
c = (omapi_connection_object_t *)h;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
REQUIRE(c->type == omapi_type_connection);
|
|
|
|
|
|
|
|
if (! force) {
|
|
|
|
/*
|
|
|
|
* If we're already disconnecting, we don't have to do
|
|
|
|
* anything.
|
|
|
|
*/
|
|
|
|
if (c->state == omapi_connection_disconnecting)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) == 0) {
|
|
|
|
if (c->out_bytes > 0) {
|
|
|
|
c->state = omapi_connection_disconnecting;
|
|
|
|
return;
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
close(c->socket);
|
|
|
|
c->state = omapi_connection_closed;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Disconnect from I/O object, if any.
|
|
|
|
*/
|
|
|
|
if (h->outer != NULL)
|
|
|
|
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);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t
|
|
|
|
omapi_connection_require(omapi_object_t *h, unsigned int bytes) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
|
1999-10-27 22:24:32 +00:00
|
|
|
c = (omapi_connection_object_t *)h;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
c->bytes_needed = bytes;
|
|
|
|
if (c->bytes_needed <= c->in_bytes)
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
return (ISC_R_NOTYET);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* 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) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
|
1999-10-27 22:24:32 +00:00
|
|
|
c = (omapi_connection_object_t *)h;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
if (c->state != omapi_connection_connected)
|
|
|
|
return (-1);
|
|
|
|
if (c->in_bytes >= OMAPI_BUFFER_SIZE - 1 &&
|
|
|
|
c->in_bytes > c->bytes_needed)
|
|
|
|
return (-1);
|
|
|
|
return (c->socket);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* 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) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
|
1999-10-27 22:24:32 +00:00
|
|
|
c = (omapi_connection_object_t *)h;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
if (c->out_bytes)
|
|
|
|
return (c->socket);
|
1999-10-27 22:24:32 +00:00
|
|
|
else
|
1999-11-02 04:01:34 +00:00
|
|
|
return (-1);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* 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) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
|
|
|
c = (omapi_connection_object_t *)h;
|
1999-11-02 04:01:34 +00:00
|
|
|
if (c->state == omapi_connection_disconnecting && c->out_bytes == 0)
|
|
|
|
omapi_disconnect(h, OMAPI_FORCE_DISCONNECT);
|
|
|
|
if (c->state == omapi_connection_closed)
|
|
|
|
return (ISC_R_NOTCONNECTED);
|
|
|
|
return (ISC_R_SUCCESS);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
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)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
if (h->inner != NULL && h->inner->type->set_value)
|
|
|
|
return (*(h->inner->type->set_value))(h->inner, id,
|
|
|
|
name, value);
|
|
|
|
return (ISC_R_NOTFOUND);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t
|
|
|
|
omapi_connection_get_value(omapi_object_t *h, omapi_object_t *id,
|
|
|
|
omapi_data_string_t *name,
|
|
|
|
omapi_value_t **value)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
if (h->inner != NULL && h->inner->type->get_value)
|
|
|
|
return (*(h->inner->type->get_value))(h->inner, id,
|
|
|
|
name, value);
|
|
|
|
return (ISC_R_NOTFOUND);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
void
|
|
|
|
omapi_connection_destroy(omapi_object_t *h, const char *name) {
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_connection_object_t *c;
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
|
|
|
|
c = (omapi_connection_object_t *)h;
|
|
|
|
|
|
|
|
if (c->state == omapi_connection_connected)
|
|
|
|
omapi_disconnect(h, OMAPI_FORCE_DISCONNECT);
|
|
|
|
|
|
|
|
if (c->listener != NULL)
|
|
|
|
omapi_object_dereference(&c->listener, name);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t
|
|
|
|
omapi_connection_signal_handler(omapi_object_t *h, const char *name,
|
|
|
|
va_list ap)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
if (h->inner != NULL && h->inner->type->signal_handler)
|
|
|
|
return (*(h->inner->type->signal_handler))(h->inner, name, ap);
|
|
|
|
|
|
|
|
return (ISC_R_NOTFOUND);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Write all the published values associated with the object through the
|
|
|
|
* specified connection.
|
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t
|
|
|
|
omapi_connection_stuff_values(omapi_object_t *c, omapi_object_t *id,
|
|
|
|
omapi_object_t *h)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
1999-11-02 04:01:34 +00:00
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
if (h->inner != NULL && h->inner->type->stuff_values)
|
|
|
|
return ((*(h->inner->type->stuff_values))(c, id, h->inner));
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|